package cn.melonlib.nars.names.comp.service;

import cn.hutool.core.bean.BeanUtil;
import cn.melonlib.cores.aop.anno.inject.FilterRecord;
import cn.melonlib.cores.aop.anno.inject.FilterTenant;
import cn.melonlib.cores.aop.anno.inject.InjectRecord;
import cn.melonlib.cores.commons.comp.dao.DaoUtils;
import cn.melonlib.cores.commons.comp.enums.OrderByType;
import cn.melonlib.cores.commons.comp.service.impl.WorkerBasicService;
import cn.melonlib.cores.commons.exceptions.WorkerServiceException;
import cn.melonlib.cores.identification.comp.messenger.MessengerUtils;
import cn.melonlib.cores.identification.model.entity.Identification;
import cn.melonlib.cores.identification.model.entity.Organization;
import cn.melonlib.cores.jpa.model.bean.collections.JsonMap;
import cn.melonlib.cores.jpa.model.bean.impl.DefaultQuery;
import cn.melonlib.cores.jpa.model.bean.impl.OffsetQueryImpl;
import cn.melonlib.cores.jpa.model.bean.impl.PaginationList;
import cn.melonlib.cores.model.anno.*;
import cn.melonlib.cores.model.input.OrderByInput;
import cn.melonlib.nars.action.model.entity.NamesRecycleBin;
import cn.melonlib.nars.names.model.enmus.NamesType;
import cn.melonlib.nars.names.model.entity.MelonDirectory;
import cn.melonlib.nars.names.model.entity.MelonFile;
import cn.melonlib.nars.names.model.entity.MelonNames;
import cn.melonlib.nars.names.model.views.MelonNamesExtVo;
import cn.melonlib.nars.names.model.views.MelonNamesVo;
import com.fasterxml.jackson.core.JsonProcessingException;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.intellij.lang.annotations.Language;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.io.File;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static cn.melonlib.cores.commons.comp.service.impl.WorkerBasicService.ROOT_METHOD.*;

@Service
@WorkerService(
        module = "names"
        ,model = @WorkerModel(
                modelType = MelonNames.class
                ,voType = MelonNamesVo.class
                ,extendsTypes = {MelonNamesExtVo.class}
)
        ,ignores = {SEARCH,LIST,SAVE,REMOVE_BY_ID,REMOVE_BY_ID_LIST,LOAD_ALL,TOTAL_COUNT,GET_BY_ID}
)
@WorkerComment("名称服务")
@Data
@EqualsAndHashCode(callSuper = true)
@FilterRecord
@FilterTenant
public class NamesService extends WorkerBasicService<MelonNames> {

    @Value("${system.config.storeRootPath:/service/resources}")
    private String storeRootPath;

    @Resource
    private MessengerUtils messengerUtils;

    @Resource
    private DaoUtils daoUtils;

    @WorkerQuery
    @WorkerComment("查找资源")
    public MelonNames lookup(@WorkerParameter(value = "path",comment = "资源路径",required = true) String path){
        Assert.isTrue(path.startsWith("/"),"路径格式不正确");
        Identification identification=getIdentificationMessenger().getIdentification();
        Organization organization=getIdentificationMessenger().getOrganization();
        @Language("HQL") String query="from "+MelonNames.class.getName()+" where path=?1 and creatorId=?2 and tenantId=?3";
        List<MelonNames> names=dao.<List<MelonNames>,
                DefaultQuery>search(new DefaultQuery(query,
                List.of(path, BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id")))).getResult();
        if(names.isEmpty()){
            return null;
        }
        return names.get(0);
    }

    @WorkerQuery
    @WorkerComment("查询目录")
    public List<MelonNamesVo> dir(@WorkerParameter(value = "path",comment = "目录路径",required = true)String path,
                                @WorkerParameter(value = "first",comment = "获取数量",required = true) Integer first,
                                @WorkerParameter(value = "offset",comment = "获取偏移",required = true) Long offset,
                                @WorkerParameter(value = "filter",comment = "过滤条件",required = true) JsonMap<JsonMap<Object>> filter,
                                @WorkerParameter(value = "orderBy",comment = "排序",required = false)OrderByInput orderBy
                                ){
        Assert.isTrue(path.startsWith("/"),"路径格式不正确");
        Identification identification=getIdentificationMessenger().getIdentification();
        Organization organization=getIdentificationMessenger().getOrganization();
        JsonMap<OrderByType> orderByJson=Objects.nonNull(orderBy)?JsonMap.<OrderByType>createMap().append(orderBy.getField(),OrderByType.valueOf(orderBy.getType())):defaultOrderBy();
        if(Objects.equals("/",path)){
            return dao.<List<MelonNames>,OffsetQueryImpl>
                            search(new OffsetQueryImpl("from "+MelonNames.class.getName()+" where parent is null and "+daoUtils.filterToQl((JsonMap) filter)+" order by createDateTime desc"
                            , daoUtils.filterParams((JsonMap)filter),offset,first))
                    .getResult().stream().map(MelonNamesVo::new).collect(Collectors.toList());
        }
        @Language("HQL") String query="from MelonDirectory where path=?1 and creatorId=?2 and tenantId=?3";
        List<MelonDirectory> directories=dao.
                <List<MelonDirectory>,DefaultQuery>search(new DefaultQuery(query,
                List.of(path,BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id"))))
                .getResult();
        Assert.notEmpty(directories,"找不到对应的目录");
        filter.append("parent",JsonMap.<Object>createMap().append("eq",directories.get(0)));
        ;
        List<? extends Serializable> params=(List<? extends Serializable>) Stream.concat(Stream.of(directories.get(0).getId()),daoUtils.filterParams( (JsonMap)filter).stream()).collect(Collectors.toList());
        return dao.<List<MelonNames>,OffsetQueryImpl>
                search(new OffsetQueryImpl("from "+MelonNames.class.getName()+" where parent.id=?1 and "+daoUtils.filterToQl((JsonMap)filter,2)
                        ,params,offset,first))
                .getResult().stream().map(MelonNamesVo::new).collect(Collectors.toList());
    }

    @WorkerStore(successTip = "创建成功",errorTip = "创建失败")
    @WorkerComment("创建目录")
    @Transactional(value = Transactional.TxType.REQUIRED,rollbackOn = Exception.class)
    public MelonNames mkdir(@WorkerParameter(value = "path",comment = "目录路径",required = true) String path){
        Assert.isTrue(path.startsWith("/"),"路径必须以'/'开头");
        Identification identification=getIdentificationMessenger().getIdentification();
        Organization organization=getIdentificationMessenger().getOrganization();
        return Arrays.stream(path.substring(1).split("/")).filter(StringUtils::isNotBlank).reduce(new MelonDirectory(),(parent,name)->{
                String p = (StringUtils.isNotBlank(parent.getId()) ? parent.getPath() : "") + "/" + name;
                List<MelonNames> names = dao.<List<MelonNames>, DefaultQuery>search(new DefaultQuery("from " + MelonNames.class.getName() + " where path=?1 and creatorId=?2", List.of(p
                ,BeanUtil.getProperty(identification,"id")
                ))).getResult();
                if(names.isEmpty()) {
                    MelonDirectory directory = new MelonDirectory();
                    directory.setName(name);
                    directory.setPath((parent.getPath()==null?"":parent.getPath()) + "/" + name);
                    directory.setParent(StringUtils.isNotBlank(parent.getId()) ? parent : null);
                    directory.setNamesType(NamesType.DIRECTORY);
                    try {
                        messengerUtils.setMessengers(directory);
                    } catch (Exception e) {
                        throw new WorkerServiceException("记录信息失败");
                    }
                    return dao.save(directory).getResult();
                }else if(Objects.equals(names.get(0).getNamesType(),NamesType.DIRECTORY)){
                    return (MelonDirectory) names.get(0);
                }else{
                    throw new WorkerServiceException("路径已被占用，创建失败");
                }
        },(p,n)->null);
    }

    @WorkerStore(successTip = "删除成功",errorTip = "删除失败")
    @WorkerComment("彻底删除资源")
    @Transactional(value = Transactional.TxType.REQUIRED,rollbackOn = Exception.class)
    public int removePath(@WorkerParameter(value = "paths",comment = "路径",required = true) List<String> paths){
        return paths.stream().filter(StringUtils::isNotBlank).reduce(0,(row,path)->{
            Assert.notNull(path,"请输入路径");
            Assert.isTrue(path.startsWith("/"),"必须以'/'开头");
            Identification identification=getIdentificationMessenger().getIdentification();
            Organization organization=getIdentificationMessenger().getOrganization();
            List<MelonNames> names=dao.<List<MelonNames>,DefaultQuery>search(new DefaultQuery("from "+MelonNames.class.getName()+" where path=?1 and creatorId=?2 and tenantId=?3",List.of(path
                    ,BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id")
            ))).getResult();
            if(Objects.equals(names.get(0).getNamesType(),NamesType.DIRECTORY)){
                List<MelonFile> files = dao.<List<MelonFile>,DefaultQuery>search(new DefaultQuery("from MelonFile where path like ?1 and creatorId=?2 and tenantId=?3",List.of(path+"/%",
                        BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id")
                ))).getResult();
                files.forEach(f->FileUtils.deleteQuietly(new File(f.getStorePath())));
                row +=dao.updateQl("delete from "+MelonNames.class.getName()+" where path like ?1 and creatorId=?2 and tenantId=?3",List.of(path+"/%",
                        BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id")
                ));
                row += dao.remove(names.get(0)).getResult();
                return row;
            }else{
                if(Objects.equals(names.get(0).getNamesType(),NamesType.FILE)){
                    FileUtils.deleteQuietly(new File(((MelonFile)names.get(0)).getStorePath()));
                }
                return row+dao.remove(names.get(0)).getResult();
            }
        },(r,p)->null);

    }

    @WorkerQuery
    @WorkerComment("获取目录下所有文件")
    public List<MelonNames> findAllFiles(@WorkerParameter(value = "path",comment = "路径",required = true) String path){
        MelonNames names=lookup(path);
        if(Objects.equals(names.getNamesType(),NamesType.DIRECTORY)){
            Identification identification=getIdentificationMessenger().getIdentification();
            Organization organization=getIdentificationMessenger().getOrganization();
            return this.dao.<List<MelonNames>,DefaultQuery>search(new DefaultQuery("from "+MelonNames.class.getName()+" where path like ?1 and creator=?2 and tenantId=?3 and namesType!=?4 "
                    ,List.of(path+"/%",BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id"),
                    NamesType.DIRECTORY.name()))).getResult();
        }else{
            return List.of(names);
        }

    }

    @WorkerQuery
    @WorkerComment("获取文件")
    public PaginationList<MelonNames> findFiles(@WorkerParameter(value = "path",comment = "路径",required = true) String path,
                                                @WorkerParameter(value = "first",comment = "数量",required = true) Integer first,
                                                @WorkerParameter(value = "offset",comment = "偏移",required = true) Long offset){
        MelonNames names=lookup(path);
        Identification identification=getIdentificationMessenger().getIdentification();
        Organization organization=getIdentificationMessenger().getOrganization();
        if(Objects.equals(names.getNamesType(),NamesType.DIRECTORY)){
            return this.dao.<PaginationList<MelonNames>,OffsetQueryImpl>search(new OffsetQueryImpl("from "+MelonNames.class.getName()+" where path like ?1 and creator=?2 and tenantId=?3 and namesType!=?4"
                    ,List.of(path+"/%",BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id"),
                    NamesType.DIRECTORY.name()),offset,first)).getResult();
        }else{
            return PaginationList.createOffset(List.of(names),0,1,1);
        }

    }

    @WorkerStore(successTip = "删除成功",errorTip = "删除失败")
    @WorkerComment("移至回收站")
    @Transactional(value = Transactional.TxType.REQUIRED,rollbackOn = Exception.class)
    public int moveToRecycleBin(@WorkerParameter(value = "path",comment = "资源路径",required = true) List<String> paths){
        return paths.stream().reduce(0,(row,path)->{
            MelonNames names=lookup(path);
            if(names instanceof MelonDirectory){
                Identification identification=getIdentificationMessenger().getIdentification();
                Organization organization=getIdentificationMessenger().getOrganization();
                List<MelonNames> melonNamesList=this.dao.<List<MelonNames>,DefaultQuery>search(new DefaultQuery("from "+MelonNames.class.getName()+" where path like ?1 and creator=?2 and tenantId=?3 and namesType!=?4"
                        ,List.of(path+"/%",BeanUtil.getProperty(identification,"id"),BeanUtil.getProperty(organization,"id"),
                        NamesType.DIRECTORY.name()))).getResult();
                return Stream.concat(melonNamesList.stream(),Stream.of(names)).reduce(row,(r,n)->{
                    NamesRecycleBin namesRecycleBin= new NamesRecycleBin();
                    BeanUtils.copyProperties(n,namesRecycleBin);
                    try {
                        namesRecycleBin.setBody(getJsonMapper().writeValueAsString(n));
                    } catch (JsonProcessingException e) {
                        throw new WorkerServiceException(e.getMessage(),e);
                    }
                    return r+1;
                },(r,n)->null);
            }else{
                NamesRecycleBin namesRecycleBin= new NamesRecycleBin();
                BeanUtils.copyProperties(names,namesRecycleBin);
                try {
                    namesRecycleBin.setBody(getJsonMapper().writeValueAsString(names));
                } catch (JsonProcessingException e) {
                    throw new WorkerServiceException(e.getMessage(),e);
                }
                return row+1;
            }


        },(r,p)->null);
    }

}
